﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Rappelz.GameServer
{
    public class Inventory
    {
// UserDefinedType: StructInventory
// VTable         :   , Type:  *
        public interface InventoryEventReceiver
        {
            void onAdd(Inventory pInventory, Item pItem, bool bSkipUpdateItemToDB);
            void onRemove(Inventory pInventory, Item pItem, bool bSkipUpdateItemToDB);
            void onChangeCount(Inventory pInventory, Item pItem, bool bSkipUpdateItemToDB);
        }

        public class InventorySorter : IComparer<Item>
        {
            public static int[] aTypeOrder = { 7, 0, 1, 6, 2, 3, 5, 4 };

            public InventorySorter()
            {
            }

            // 5. Write the Compare method with a switch statement
            public int Compare(Item lh, Item rh)
            {
                int v4;
                int v5;
                if (lh.IsSameObject(rh))
                    return 0;

                if (lh.m_pItemBase.nType > 7 )
                    v4 = 8;
                else
                    v4 = aTypeOrder[lh.m_pItemBase.nType];
                if (rh.m_pItemBase.nType > 7 )
                    v5 = 8;
                else
                    v5 = aTypeOrder[rh.m_pItemBase.nType];
                return (v4 < v5 || v4 == v5 && lh.m_Instance.Code <= rh.m_Instance.Code) ? -1 : 1;

            }
        }

// VTable         :     , Type:  *
// Function       :   public void StructInventory(const struct StructInventory &)
// Function       :   public void StructInventory::StructInventory(struct StructInventory::InventoryEventReceiver *)
// Function       :   public void StructInventory::~StructInventory()
// Function       :   public void SetEventReceiver(struct StructInventory::InventoryEventReceiver *)
        public Item Push(Item item, long cnt, bool bSkipUpdateItemToDB)
        {
            if (item.IsJoinable())
            {
                Item ji = this.FindByCode(item.m_Instance.Code);
                if(ji != null)
                {
                    m_fWeight = item.m_pItemBase.fWeight * (float)cnt + m_fWeight;
                    if (ji.m_Instance.nWearInfo != ItemBase.ItemWearType.WearNone)
                        m_fWeightModifier = m_fWeightModifier - ji.m_pItemBase.fWeight * (float)cnt;
                    long new_cnt = cnt + ji.m_Instance.nCount;
                    this.setCount(ji, new_cnt, bSkipUpdateItemToDB);
                    return ji;
                }
            }

            m_fWeight = item.GetWeight() + m_fWeight;
            if (item.m_Instance.nWearInfo != ItemBase.ItemWearType.WearNone)
            {
                m_fWeightModifier -= item.GetWeight();
            }
            this.push(item, bSkipUpdateItemToDB);
            if (item.m_Instance.nIdx > m_nIndex )
                m_nIndex = item.m_Instance.nIdx;

            return item;

        }

        public Item Pop(Item pItem, long cnt, bool bSkipUpdateItemToDB)
        {
            long new_cnt;
            Item pNewItem;

            this.check(pItem);
            if (pItem.m_Instance.nCount == cnt)
            {
                this.m_fWeight = this.m_fWeight - pItem.m_pItemBase.fWeight * cnt;
                if (pItem.m_Instance.nWearInfo != ItemBase.ItemWearType.WearNone)
                    this.m_fWeightModifier = pItem.GetWeight() + this.m_fWeightModifier;
                this.pop(pItem, bSkipUpdateItemToDB);
                return pItem;
            }
            else if (pItem.m_Instance.nCount > cnt)
            {
                cnt = 0;
                new_cnt = 1;
                pNewItem = Item.AllocItem(0,pItem.m_Instance.Code,new_cnt,ItemInstance.GenerateCode.ByDivide,-1,-1,-1,-1,0,0,0,0,-1,Elemental.Type.TypeNone,0,0,0);
                pNewItem.CopyFrom(pItem);
                pNewItem.SetCount(cnt);
                this.m_fWeight = this.m_fWeight - pItem.m_pItemBase.fWeight * cnt;
                if (pItem.m_Instance.nWearInfo != ItemBase.ItemWearType.WearNone)
                    this.m_fWeightModifier = pItem.m_pItemBase.fWeight * cnt + this.m_fWeightModifier;
                new_cnt = pItem.m_Instance.nCount - cnt;
                this.setCount(pItem, new_cnt, bSkipUpdateItemToDB);
                return pNewItem;
            }
            return null;
        }

// Function       :   public struct StructItem * Get(unsigned int)

        public bool Erase(Item pItem, long count, bool bSkipUpdateItemToDB)
        {
            if ( !this.check(pItem))
                return false;

            if (pItem.m_Instance.nCount <= count)
            {
                this.m_fWeight -= pItem.GetWeight();
                if (pItem.m_Instance.nWearInfo != ItemBase.ItemWearType.WearNone)
                    this.m_fWeightModifier = pItem.GetWeight() + this.m_fWeightModifier;
                this.pop(pItem, bSkipUpdateItemToDB);
                Item.PendFreeItem(pItem);
                return false;
            }

            this.m_fWeight -= pItem.m_pItemBase.fWeight * count;
            if (pItem.m_Instance.nWearInfo != ItemBase.ItemWearType.WearNone)
                this.m_fWeightModifier += (pItem.m_pItemBase.fWeight * count);
            long nc = pItem.m_Instance.nCount - count;
            this.setCount(pItem, nc, bSkipUpdateItemToDB);
            return true;
        }

        public Item Find(int code, uint flag, bool bFlag)
        {
            foreach (Item i in m_vList)
            {
                bool isflagged = (flag & i.m_Instance.Flag) != 0;

                if(i.m_Instance.Code == code)
                {
                    if (bFlag)
                    {
                        if (isflagged)
                            return i;
                    }
                    else
                    {
                        if (!isflagged)
                            return i;
                    }
                }
            }
            return null;
        }
    
        public Item FindByCode(int code)
        {
            foreach (Item i in m_vList)
            {
                if(i.m_Instance.Code == code)
                    return i;
            }
            return null;
        }

        public Item FindBySID(long uid)
        {
            foreach (Item i in m_vList)
            {
                if (i.m_Instance.UID == uid)
                    return i;
            }
            return null;
        }
// Function       :   public const __int64 StructInventory::GetItemCount(int)
// Function       :   public unsigned int GetCount()
// Function       :   public float GetWeight()
// Function       :   public float GetWeightModifier()
// Function       :   public void SetWeightModifier(float)
// Function       :   public void AddWeightModifier(float)
// Function       :   public bool IsValid(struct StructItem *)
// Function       :   public const class std::vector<StructItem *,std::allocator<StructItem *> > & GetExpireItemList()
// Function       :   public int IssueNewIndex()
// Function       :   public static const bool StructInventory::_ItemArrangeGreater(const struct StructItem *, const struct StructItem *)

        public void Arrange()
        {
            List<Item> vSortBuffer = new List<Item>();  // -52  -34h

            this.m_vList.Sort(new InventorySorter());
            this.m_nIndex = 0;

            foreach (Item item in this.m_vList)
            {
                this.m_nIndex++;
                if(item.m_Instance.nIdx != this.m_nIndex)
                {
                    item.m_Instance.nIdx = this.m_nIndex;
                    item.m_bIsNeedUpdateToDB = true;
                }
            }
        }

        private void setCount(Item item, long new_cnt, bool bSkipUpdateItemToDB)
        {
            item.SetCount(new_cnt);

            if (this.m_pEventReceiver != null)
                this.m_pEventReceiver.onChangeCount(this, item,bSkipUpdateItemToDB);
        }
        
        private void push(Item item, bool bSkipUpdateItemToDB)
        {
            m_vList.Add(item);
            if (item.IsExpireItem())
                m_vExpireItemList.Add(item);
            item.m_unInventoryIndex = (uint) m_vList.Count - 1;
            if (this.m_pEventReceiver != null)
                this.m_pEventReceiver.onAdd(this, item, bSkipUpdateItemToDB);
        }

        private void pop(Item pItem, bool bSkipUpdateItemToDB)
        {
            if (this.m_pEventReceiver != null)
                this.m_pEventReceiver.onRemove(this, pItem, bSkipUpdateItemToDB);

            Item last = this.m_vList.Last();
            if (last.m_hHandle != pItem.m_hHandle)
            {
                int idx = (int)last.m_unInventoryIndex;
                last.m_unInventoryIndex = pItem.m_unInventoryIndex;
                this.m_vList[(int)last.m_unInventoryIndex] = last;
                this.m_vList.RemoveAt(idx);
            }
            if(this.m_vExpireItemList.Contains(pItem))
                this.m_vExpireItemList.Remove(pItem);
        }

        public bool check(Item pItem)
        {
            bool result;
            if (pItem.m_unInventoryIndex < this.m_vList.Count)
                result = ReferenceEquals(this.m_vList[(int)pItem.m_unInventoryIndex], pItem);
            else
                result = false;
            return result;
        }

        public InventoryEventReceiver m_pEventReceiver;                 // 0x4
        public float m_fWeight;                                         // 0x8
        public float m_fWeightModifier;                                 // 0xC
        public List<Item> m_vList = new List<Item>();                   // 0x10
        public List<Item> m_vExpireItemList = new List<Item>();         // 0x20 
        public int m_nIndex;                                            // 0x30
// Function       :   public struct StructInventory & operator=(const struct StructInventory &)

    }
}
